package com.moro.lisp.os;

import java.util.ArrayList;
import java.util.List;

import com.moro.lisp.JLispData;
import com.moro.lisp.JLispFunctionName;
import com.moro.lisp.JLispMacroName;
import com.moro.lisp.JLispType;
import com.moro.lisp.tools.FunctionPool;
import com.moro.lisp.tools.MacroPool;
import com.moro.lisp.tools.SymbolPool;
import com.moro.lisp.typedefine.Atom;
import com.moro.lisp.typedefine.Char;
import com.moro.lisp.typedefine.Cons;
import com.moro.lisp.typedefine.Function;
import com.moro.lisp.typedefine.LString;
import com.moro.lisp.typedefine.Macro;
import com.moro.lisp.typedefine.Error;

public class Terminal {
	public static List<String> messages = new ArrayList<String>();
	private static int messagesSize = 3;
	private static String consStart = "(";
	private static String consEnd = ")";
	public static void print(JLispData data) {//在终端上打印数据信息，这里可以是任何数据类型
		if(JLispType.STRING == data.type) {
			LString str = (LString)(data.data);
			System.out.print(str.text);
		}else if(JLispType.ATOM == data.type) {
			Atom atom = (Atom)(data.data);
			if("\\n".equalsIgnoreCase(atom.name))
				System.out.println();
			else {
				if(null == atom.isClosureAtom) {
					System.out.print(atom.name);
				}else {
					System.out.print('~' + atom.name);
				}
			}
		}else if(JLispType.CONS == data.type) {
			Cons cons = (Cons)(data.data);
			printCons(cons);
		}else if(JLispType.CHAR == data.type) {
			Char ch = (Char)(data.data);
			System.out.print(ch.ch + "");
		}else if(JLispType.FUNCTION == data.type) {
			Function function = (Function)(data.data);
			if(JLispFunctionName.CUSTOMIZE == function.functionName) {
				System.out.print("FUN<CUSTOMIZE>"+function.toString());
			}else {
				System.out.print("FUN<"+function.functionName+">basic");
			}
		}else if(JLispType.MACRO == data.type) {
			Macro macro = (Macro)(data.data);
			if(JLispMacroName.CUSTOMIZE == macro.macroName) {
				System.out.print("MACRO<CUSTOMIZE>"+macro.toString());
			}else {
				System.out.print("MACRO<"+macro.macroName+">basic");
			}
		}else {
			System.out.print("ERROR("+((Error)(data.data)).getErrorText()+")");
		}
	}
	private static void printCons(Cons cons) {
		System.out.print(Terminal.consStart);
		for(int i = 0;i < cons.getLength();i++) {
			if(i > 9) {
				System.out.print("...");
				break;
			}
			if(i == cons.getLength() - 1) {
				print(cons.getItemById(i));
			}else {
				print(cons.getItemById(i));
				System.out.print(" ");
			}
		}
		System.out.print(Terminal.consEnd);
	}
	public static String getStringFromData(JLispData data) {
		if(JLispType.STRING == data.type) {
			LString str = (LString)(data.data);
			return "$"+str.text;
		}else if(JLispType.ATOM == data.type) {
			Atom atom = (Atom)(data.data);
			return atom.name;
		}else if(JLispType.CONS == data.type) {
			Cons cons = (Cons)(data.data);
			String consStr = Terminal.consStart;
			for(int i = 0;i < cons.getLength();i++) {
				if(i == cons.getLength() - 1) {
					consStr += getStringFromData(cons.getItemById(i));
				}else {
					consStr += getStringFromData(cons.getItemById(i)) + " ";
				}
			}
			consStr += Terminal.consEnd;
			return consStr;
		}else if(JLispType.CHAR == data.type) {
			Char ch = (Char)(data.data);
			return "#"+ch.ch;
		}else if(JLispType.FUNCTION == data.type) {
			Function function = (Function)(data.data);
			if(JLispFunctionName.CUSTOMIZE == function.functionName) {
				return "FUN<CUSTOMIZE>"+function.toString();
			}else {
				return "FUN<"+function.functionName+">basic";
			}
		}else if(JLispType.MACRO == data.type) {
			Macro macro = (Macro)(data.data);
			if(JLispMacroName.CUSTOMIZE == macro.macroName) {
				return "MACRO<CUSTOMIZE>"+macro.toString();
			}else {
				return "MACRO<"+macro.macroName+">basic";
			}
		}else {
			return "ERROR("+((Error)(data.data)).getErrorText()+")";
		}
	}
	public static void errorPrint(String errorText) {//在终端上打印错误信息，注意这里必须是字符串类型
		Terminal.makeMessage("warning: "+errorText);
	}
	public static void setConsStartAndEnd(String startAndEnd) {
		int length = startAndEnd.length();
		if(length < 2) {
			return;
		}
		consStart = startAndEnd.substring(0, length / 2);
		consEnd = startAndEnd.substring(length / 2);
	}
	public static JLispData getDataFromString(String str, SymbolPool symbolPool, FunctionPool functionPool, MacroPool macroPool) {//从字符串获取JLispData数据,字符串必须是经过去空格处理后的字符串
		if(str.startsWith("(")) {//说明是列表
			if(!str.endsWith(")")) {
				return new JLispData(new Error("Error Input!"), JLispType.ERROR);
			}
			List<JLispData> datasList = new ArrayList<JLispData>();
			String itemStrs = str.substring(1, str.length() - 1).trim();//去括号+去空格
			if("".equals(itemStrs)) {
				return new JLispData(symbolPool.getSymbolByString("nil"), JLispType.ATOM);
			}
			int bracketOrder = 0;//左括号的数目，一个右括号可以抵消一个左括号
			int consStartIndex = -1;
			String[] strs = itemStrs.split(" ");//通过空格分割
			for(int i = 0;i < strs.length;i++) {
				if("".equals(strs[i])) {
					continue;
				}
				bracketOrder += getLeftBracketSize(strs[i]);
				if(bracketOrder == 0) {
					if(consStartIndex == -1) {
						datasList.add(getDataFromString(strs[i], symbolPool, functionPool, macroPool));
					}else {
						String cons = "";
						for(int j = consStartIndex;j <= i;j++) {
							if("".equals(strs[j])) {
								continue;
							}
							if(j == i) {
								cons += strs[j];
							}else {
								cons += strs[j]+" ";
							}
						}
						datasList.add(getDataFromString(cons, symbolPool, functionPool, macroPool));
						consStartIndex = -1;
					}
					
				}else {
					if(consStartIndex == -1) {
						consStartIndex = i;
					}
				}
			}
			if(datasList.size() == 0) {
				return new JLispData(symbolPool.getSymbolByString("nil"), JLispType.ATOM);
			}
			JLispData[] items = new JLispData[datasList.size()];
			for(int i = 0;i < datasList.size();i++) {
				items[i] = datasList.get(i);
			}
			return new JLispData(new Cons(items, symbolPool.getSymbolByString("nil")), JLispType.CONS);
		}else {
			if(str.startsWith("#")) {//说明是字符
				String ch = str.substring(1);
				if(ch.length() != 1) {
					return new JLispData(new Error("Type Error!"), JLispType.ERROR);
				}
				return new JLispData(Char.getChar(ch.charAt(0)), JLispType.CHAR);
			}else if(str.startsWith("$")) {//说明是字符串
				return new JLispData(symbolPool.getSymbolByString("nil"), JLispType.ATOM);
			}else if(str.startsWith("'")) {//说明是列表
				return new JLispData(new Cons(new JLispData[]{new JLispData(symbolPool.getSymbolByString("quote"), JLispType.ATOM),
						Terminal.getDataFromString(str.substring(1).trim(), symbolPool, functionPool, macroPool)}, symbolPool.getSymbolByString("nil")), JLispType.CONS);
			}else if(str.startsWith("fn'")) {//说明是求函数值的函数
				return new JLispData(new Cons(new JLispData[]{new JLispData(symbolPool.getSymbolByString("symbolfunction"), JLispType.ATOM),
						Terminal.getDataFromString("'"+str.substring(3).trim(), symbolPool, functionPool, macroPool)}, symbolPool.getSymbolByString("nil")), JLispType.CONS);
			}else if(str.startsWith("\"")) {//宏展开函数
				return new JLispData(new Cons(new JLispData[]{new JLispData(symbolPool.getSymbolByString("macroexpand"), JLispType.ATOM),
						Terminal.getDataFromString("'"+str.substring(1).trim(), symbolPool, functionPool, macroPool)}, symbolPool.getSymbolByString("nil")), JLispType.CONS);
			}else if(str.startsWith("v'")) {//说明是求值的函数
				return new JLispData(new Cons(new JLispData[]{new JLispData(symbolPool.getSymbolByString("symbolvalue"), JLispType.ATOM),
						Terminal.getDataFromString("'"+str.substring(2).trim(), symbolPool, functionPool, macroPool)}, symbolPool.getSymbolByString("nil")), JLispType.CONS);
			}else if(str.equals("?")) {//表示开启debug
				return new JLispData(new Cons(new JLispData[]{new JLispData(symbolPool.getSymbolByString("debug"), JLispType.ATOM),
						Terminal.getDataFromString("-3", symbolPool, functionPool, macroPool)}, symbolPool.getSymbolByString("nil")), JLispType.CONS);
			}else if(str.equals("!")) {//表示关闭debug
				return new JLispData(new Cons(new JLispData[]{new JLispData(symbolPool.getSymbolByString("debug"), JLispType.ATOM),
						Terminal.getDataFromString("-4", symbolPool, functionPool, macroPool)}, symbolPool.getSymbolByString("nil")), JLispType.CONS);
			}else if(str.startsWith("`")) {//说明是列表
				return new JLispData(new Cons(new JLispData[]{new JLispData(symbolPool.getSymbolByString("reversequote"), JLispType.ATOM),
						Terminal.getDataFromString(str.substring(1).trim(), symbolPool, functionPool, macroPool)}, symbolPool.getSymbolByString("nil")), JLispType.CONS);
			}else if(str.startsWith(",")) {//说明是逗号宏或者逗号分解宏
				if(str.startsWith(",@")) {//说明是逗号分解宏
					return new JLispData(new Cons(new JLispData[]{new JLispData(symbolPool.getSymbolByString("commat"), JLispType.ATOM),
							Terminal.getDataFromString(str.substring(2).trim(), symbolPool, functionPool, macroPool)}, symbolPool.getSymbolByString("nil")), JLispType.CONS);
				}else {//逗号宏
					return new JLispData(new Cons(new JLispData[]{new JLispData(symbolPool.getSymbolByString("comma"), JLispType.ATOM),
							Terminal.getDataFromString(str.substring(1).trim(), symbolPool, functionPool, macroPool)}, symbolPool.getSymbolByString("nil")), JLispType.CONS);
				}
			}else {//最后才是原子
				if(str.contains(">")) {
					int index = str.indexOf(">");
					if(0 != index && str.length() - 1 != index) {
						String objName = str.substring(0, index);
						String keyName = str.substring(index + 1);
						return new JLispData(new Cons(new JLispData[]{new JLispData(symbolPool.getSymbolByString("get"), JLispType.ATOM),
								new JLispData(symbolPool.getSymbolByString(objName), JLispType.ATOM),
								new JLispData(new Cons(new JLispData[]{new JLispData(symbolPool.getSymbolByString("quote"), JLispType.ATOM),
										Terminal.getDataFromString(keyName, symbolPool, functionPool, macroPool)}, symbolPool.getSymbolByString("nil")),
										JLispType.CONS)}, symbolPool.getSymbolByString("nil")), JLispType.CONS);
					}
				}else if(str.contains(":")) {
					int index = str.indexOf(":");
					if(0 != index && str.length() - 1 != index) {
						String objName = str.substring(0, index);
						String funName = str.substring(index + 1);
						Cons asFunCons = new Cons(new JLispData[]{new JLispData(symbolPool.getSymbolByString("get"), JLispType.ATOM),
								new JLispData(symbolPool.getSymbolByString(objName), JLispType.ATOM),
								new JLispData(new Cons(new JLispData[]{new JLispData(symbolPool.getSymbolByString("quote"), JLispType.ATOM),
										Terminal.getDataFromString(funName, symbolPool, functionPool, macroPool)}, symbolPool.getSymbolByString("nil")),
										JLispType.CONS)}, symbolPool.getSymbolByString("nil"));
						asFunCons.isFunExpr = true;
						asFunCons.self = new JLispData(symbolPool.getSymbolByString(objName), JLispType.ATOM);
						return new JLispData(asFunCons, JLispType.CONS);
					}
				}
				if("".equals(str)) {
					return new JLispData(symbolPool.getSymbolByString("nil"), JLispType.ATOM);
				}
				return new JLispData(symbolPool.getSymbolByString(str), JLispType.ATOM);
			}
		}
	}
	private static int getLeftBracketSize(String str) {//"(abc"返回1， "abc)"返回-1， "(abc)"返回0，"ab(c"返回0（这被认为不合法），"ab)c"返回0（这被认为不合法）
		if("".equals(str)) {
			return 0;
		}
		if(str.startsWith("'") || str.startsWith("`")) {
			return getLeftBracketSize(str.substring(1));
		}
		if(str.startsWith(",")) {
			if(str.startsWith(",@")) {
				return getLeftBracketSize(str.substring(2));
			}
			return getLeftBracketSize(str.substring(1));
		}
		if(str.startsWith("(") && str.endsWith(")")) {
			return getLeftBracketSize(str.substring(1, str.length() - 1));
	
		}
		if(str.startsWith("(")) {
			return getLeftBracketSize(str.substring(1)) + 1;
			

		}else if(str.endsWith(")")) {
			return getLeftBracketSize(str.substring(0, str.length() - 1)) - 1;
		}else {//如果既不以括号开头也不以括号结尾，则一律返回0。如："a()(b"不管字符串中间有没有括号都返回0
			return 0;
		}
	}
	public static void makeMessage(String message) {
		if(Terminal.messages.size() < Terminal.messagesSize) {
			Terminal.messages.add(message);
		}else {
			for(int i = 0;i < Terminal.messagesSize - 1;i++) {
				Terminal.messages.set(i, Terminal.messages.get(i + 1));
			}
			Terminal.messages.set(Terminal.messagesSize - 1, message);
		}
	}
	public static void clearMessage() {
		Terminal.messages.clear();
	}
	public static int getMessagesSize() {
		return Terminal.messagesSize;
	}
	public static void setMessagesSize(int messagesSize) {
		Terminal.messagesSize = messagesSize;
	}
}
